package com.pan.download5.sync;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 该类主要是靠可重入锁和信号量来实现UnlockAndBlock
 */
public class LockSemaphoreUnlockAndBlock implements UnlockAndBlock{



    private Lock lock = new ReentrantLock();
    private Semaphore semaphore = new Semaphore(1);
    private ConcurrentMap<Integer,Thread> threadMap = new ConcurrentHashMap();

    //拿到内部锁后还不能放行，必须同时拿到许可证才能放行
    public void lock(){
        lock.lock();
        try {
            semaphore.acquire();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
    //释放锁后，同时归还许可证
    public void unlock(){
        lock.unlock();
        semaphore.release();
    }

    /**
     * ReentrantLock是可重入锁，拿到锁即可拿到该id阻塞的线程
     * @param id
     * @return
     */
    @Override
    public Thread getBlockerById(int id){
        lock.lock();
        try {
            Thread thread = threadMap.get(id);
            return thread;
        }finally {
            lock.unlock();
        }
    }

    //
    @Override
    public void unlockAndBlock(int id){
        lock.unlock();
        Thread blockThread = Thread.currentThread();
        threadMap.put(id,blockThread);
        new Thread(()->{
            //通过其他线程释放许可证
            Semaphore threadSemaphore = null;
            while (threadSemaphore != semaphore){
                threadSemaphore = (Semaphore) LockSupport.getBlocker(blockThread);
            }
            threadSemaphore.release();
        }).start();
        //阻塞时，传入信号量对象，由另一个线程
        LockSupport.park(semaphore);
    }

    @Override
    public void wakeBlockThread(int id) {
        lock.lock();
        if (threadMap.containsKey(id)) {
            Thread thread = threadMap.get(id);
            threadMap.remove(id);
            LockSupport.unpark(thread);
        }
        lock.unlock();
    }
}
